from Gui import *
import ctypes
import threading
import math
import time
##usb = ctypes.cdll.LoadLibrary('usb.dll')
##usb.initialize()
##buffer = ctypes.c_buffer(8)
##def set_ra1_callback():
##    usb.control_transfer(dev, 0x40, SET_RA1, 0, 0, 0, buffer)
##
##def clr_ra1_callback():
##    usb.control_transfer(dev, 0x40, CLR_RA1, 0, 0, 0, buffer)

# def get_ra2_callback():
#     usb.control_transfer(dev, 0xC0, GET_RA2, 0, 0, 1, buffer)
#     status.configure(text = 'RA2 is currently %d.' % ord(buffer[0]))

##def set_duty_callback(value):
##    usb.control_transfer(dev, 0x40, SET_DUTY, int(value), 0, 0, buffer)
##    
##def set_callback(pin):
##    global player
##    if not (pin in opponentpin or pin in playerpin):
##        usb.control_transfer(dev, 0x40, pin, 0, 0, 0, buffer)
##        img=PhotoImage(file=['X','O'][int(player)]+'.gif')
##        buttons[pin].configure(image=img)
##        buttons[pin].image=img
##        if player:
##            opponentpin.append(pin)
##        else:
##            playerpin.append(pin)
##        player = not player
##    
##def make_callback(pin):
##    return lambda: set_callback(pin)
##def update_status():
##    for pos in opponentpin:
##        usb.control_transfer(dev, 0xC0, pos, 0, 0, 1, buffer)
##    status.configure(text = 'It is %s''s turn.' % ['X', 'O'][int(player)])
##    root.after(50, update_status)
##
####def clear_board():
####        usb.control_transfer(dev, 0x40, 9, 0, 0, 0, buffer)
##
##root = Tk()
##root.title('Lab 3 GUI')
####fm = Frame(root)
####b=Button(root, width=20,text="First").grid(row=0)
####b=Button(root, width=20,text="Second").grid(row=1)
####b=Button(root, width=18,text="PAUL BOOTH").grid(row=0, column=1)
####b=Button(root, width=19,text="Blair's hair").grid(row=7, column=8)
##buttons=[]
##img=PhotoImage(file='blank.GIF')
##for i in xrange(9):
##    
####    print img
##    b=Button(root,  width=64,height=64, text=' ', image=img,command = make_callback(i))
##    b.image=img
##    b.grid(row=i/3, column=i%3)
##    buttons.append(b)
##    #b.pack(side = LEFT, padx=2, pady=2)
##    
##
###Button(fm, text = ' ', command = clr_ra1_callback).pack(side = LEFT)
### Button(fm, text = 'GET_RA2', command = get_ra2_callback).pack(side = LEFT)
###fm.pack(side = TOP)
###dutyslider = Scale(root, from_ = 0, to = 255, orient = HORIZONTAL, showvalue = FALSE, command = set_duty_callback)
###dutyslider.set(128)
###dutyslider.pack(side = TOP)
##status = Label(root, text = 'Get Ready for FUN.')
##status.grid(row=3,column=0,columnspan=3)
##b=Button(root, text = 'clear board', command = clear_board);
##b.grid(row=4,column=0,columnspan=3)
###status.pack(side = TOP)
##
##dev = usb.open_device(0x6666, 0x0003, 0)
##if dev<0:
##    print "No matching device found...\n"
##else:
##    ret = usb.control_transfer(dev, 0x00, 0x09, 1, 0, 0, buffer)
##    if ret<0:
##        print "Unable to send SET_CONFIGURATION standard request.\n"
##    clear_board()
##    root.after(30, update_status)
##    root.mainloop()
##    usb.close_device(dev)
class TowerGUI(Gui):
    def __init__(self):
        Gui.__init__(self)
        self.title('Creep Path GUI')
        self.ca_width = 1000
        self.ca_height = 800
        self.fr(LEFT, expand = 1)
        self.canvas = self.ca(width=self.ca_width, height=self.ca_height, bg='white')
        self.canvas.configure(width=self.ca_width, height=self.ca_height)
        self.canvas.bind("<Button-1>", self.clicked)
        self.canvas.pack()
        self.endfr()
        self.clicked_pos = None
        self.board=Board()
        #self.x=self.y=self.x2=self.y2=100
        self.redraw()
        thread1=threading.Thread(target=self.mainloop)
        thread1.start()
        self.infiniteloop()
        
    def redraw(self, event=None):
        #self.clear(False)
        #self.canvas.create_line(self.x,self.y,self.x2,self.y2, width=4, fill="blue")
        self.canvas.delete("all")
        self.board.draw(self.canvas)
        
        #print self.board.theta
        
    def clicked(self, event):
        self.clicked_pos = (event.x, event.y)
        print self.clicked_pos
        self.redraw()
        
    def infiniteloop(self):
        while 1:
            self.board.update()
            self.redraw()
            #time.sleep(.0001)

class Board:
    def __init__(self):
        self.creeps=[Creep(i) for i in xrange(1)]
        self.creepPath = CreepPath(rate = 0.01)
        self.towers = []
        self.theta = 0
        self.time = 0  #jankity timer solution
        self.creep_pos = []
        self.towerlist = []
      
        
    def update(self):
        self.time += 1
        for tower in self.towers:
            tower.update()
        self.towerlist.sort()
        for tower in self.towerlist:
            usb.control_transfer(dev, 0x40, AIM_TOWER, tower[0], tower[1], 0, buffer)
        


        self.creep_pos = []
        for creep in self.creeps:
            (x,y) = self.creepPath.get_xy_pos(self.time, creep.num) #How does the timer work?
            creep.x = 500+10*x
            creep.y = 500+10*y
            self.creep_pos.append((creep.x,creep.y))
            #c = towergui.canvas.create_oval((creep.x, creep.y),fill='black', width = 3)
            
            
#I added self.towerlist, lengthened self.update and messed around with towers.

        
        
            
    def draw(self, canvas):
        for tower in self.creepPath.hardgears:
            x = 500+10*tower[0]
            y = 500+10*tower[1]
            canvas.create_oval(x, y, x + tower[2], y + tower[2], fill = 'red', width = 5)
        for creep in self.creep_pos:
            canvas.create_oval((creep[0], creep[1], creep[0]+2, creep[1]+2), fill = 'red', width = 5)
##        self.oval=canvas.create_oval(500-25, 500-25,
##                               525, 525,
##                               fill="black", activefill="red")
##        self.line=canvas.create_line(500,500, 500+100*math.cos(self.theta),500+100*math.sin(self.theta), width=4, fill="blue")
                           
            
class CreepPath:
    
    def __init__(self, rate = 0.01):
        self.rate = rate
        self.delimiters=[]
        self.gears = []
##        self.hardgears = [(34.125,4.75,-1.125),(33.875,18.625,-1.125),(21.125,21.125,1.125),
##                    (35.25,33.125,-1.5),(4.6,34.875,-1.75),(14.25,14.25,2.375),
##                    (5.125,4.25,-1.25)]
        self.hardgears = [(0,0,1), (0,10,1), (10,10,1), (10,0,1)]
        self.setup_path()

    def __str__(self):
        print "I am a creep path."
        
        
##    def setup_path(self):
##        #each gear is x,y,r
##        #x,y position
##        #r is radius of the gear
##        
##        i = 0
##        for gear in self.hardgears:
##            self.gears.append(Gear(gear))
##
##        cumdist = 0
##        for i in range(len(self.gears)):
##
##            #delimiters in the form [(d1, x1, y1, th_ini, gear)]
##            from_gear = self.gears[i-2]
##            gear = self.gears[i-1]
##            to_gear = self.gears[i]
##
##            (t1, t2) = self.cctangent(from_gear, gear)
##            (t3, t4) = self.cctangent(gear, to_gear)
##            th1 = math.atan2( (t1[1] - from_gear.y) , (t1[0] - from_gear.x) )
##            th2 = math.atan2( (t2[1] - gear.y) , (t2[0] - gear.x) )
##            th3 = math.atan2( (t3[1] - gear.y) , (t3[0] - gear.x) )
##            print "th1 is (%f) , th2 is (%f) , th3 is (%f)" %(th1, th2, th3)
##            
##            self.delimiters.append((cumdist, t1[0], t1[1], th1, from_gear))
##            cumdist += math.sqrt((t2[1] - t1[1])**2 + (t2[0] - t1[0])**2)
            
##            self.delimiters.append((cumdist, t2[0], t2[1], th2, gear))
##            cumdist += gear.r*(th3 - th2)
            
                        
##            from_gear = self.gears[i-1]
##            gear = self.gears[i] 
##            try: to_gear = self.gears[i+1]
##            except: to_gear = self.gears[1]
##            (t1, t2) = self.cctangent(from_gear, gear)
##            (t3, t4) = self.cctangent(gear, to_gear)
##
##            if i == 0:
##                self.delimiters.append((0, t1[0], t1[1], 0, from_gear.r))
##                length = math.sqrt((t2[1] - t1[1])**2 + (t2[0] - t1[0])**2)
##                self.delimiters.append((length, t2[0], t2[1], 0, 0, gear.x, gear.y))
##                cumdist += length
##            
##            th1 = math.atan2( (t3[0] - gear.x) , (t3[1] - gear.y) )
##            th2 = math.atan2( (t2[0] - gear.x) , (t2[1] - gear.y) )
##            arc = gear.r*(th1 - th2)
##            
##            cumdist += abs(arc)
##
##            self.delimiters.append((cumdist, t3[0], t3[1], th1, gear.r))
##                       #total distance, x, y, initial angle, radius
##            
##            length = math.sqrt((t4[1] - t3[1])**2 + (t4[0] - t3[0])**2)
##            cumdist += abs(length)
##            self.delimiters.append((cumdist, t4[0], t4[1], 0, 0, to_gear.x, to_gear.y))
##            #This is wrong. I'm'a fix this.
##                       #total distance, x, y

    def setup_path(self):
        #each gear is x,y,r
        #x,y position
        #r is radius of the gear
        
        i = 0
        for gear in self.hardgears:
            self.gears.append(Gear(gear))

        cumdist = 0
        for i in range(len(self.gears)):

            #delimiters in the form [(d1, x1, y1, th_ini, gear)]
            from_gear = self.gears[i-2]
            gear = self.gears[i-1]
            to_gear = self.gears[i]

            (t1, t2) = self.cctangent(from_gear, gear)
            (t3, t4) = self.cctangent(gear, to_gear)
            th1 = math.atan2( (t1[1] - from_gear.y) , (t1[0] - from_gear.x) )
            th2 = math.atan2( (t2[1] - gear.y) , (t2[0] - gear.x) )
            th3 = math.atan2( (t3[1] - gear.y) , (t3[0] - gear.x) )
            
            self.delimiters.append((cumdist, t1[0], t1[1], th1, from_gear))
            cumdist += math.sqrt((t2[1] - t1[1])**2 + (t2[0] - t1[0])**2)

        for delim in self.delimiters:
            print "d is (%f) , (x,y) is (%f), %f) , th_ini is (%f)" %(delim[0], delim[1], delim[2], delim[3])
        return self.delimiters

    def cctangent(self, c1, c2):
        cen_len = math.sqrt((c2.y-c1.y)**2 + (c2.x-c1.x)**2)
        try: cen_angle = math.atan2(c2.y-c1.y,c2.x-c1.x)
        except: cen_angle = math.pi/2

        tan_len = sqrt(cen_len**2 + (c1.r - c2.r)**2)
        tan_angle = math.atan2( (c1.r - c2.r) , cen_len )

        th = cen_angle + tan_angle
        print th

        t2x = c2.x + c2.r * cos(th+math.pi/2)
        t2y = c2.y + c2.r * sin(th+math.pi/2)
        
        t1x = t2x + tan_len * cos(th)
        t1y = t2y + tan_len * sin(th)
        return ((t1x, t1y), (t2x, t2y))


    def get_xy_pos(self, t, dist_behind):
        d = self.rate * t - dist_behind
        if d >= self.delimiters[-1][0]:      #The front creep has done a lap
            d-=self.delimiters[-1][0]

        for i in range(len(self.delimiters)):
            #delimiters in the form [(d1, x1, y1, th_ini, gear)]
            pfrom = self.delimiters[i-1]
            pto = self.delimiters[i]
            
            if pfrom[0] <= d and d < pto[0]:
                if i % 1 == 0:      #even -> odd sections are linear
                    x = pfrom[1] + (d - pfrom[0]) / (pto[0] - pfrom[0]) * (pto[1] - pfrom[1])
                    y = pfrom[2] + (d - pfrom[0]) / (pto[0] - pfrom[0]) * (pto[2] - pfrom[2])
                else:               #odd -> even sections are turns
                    x = pfrom[4].x + pto[4].r * math.cos(pto[3] + (d - pfrom[0])/pto[4].r)
                    y = pfrom[4].y + pto[4].r * math.sin(pto[3] + (d - pfrom[0])/pto[4].r)
                return x, y
        return 0,0

class Gear:
    def __init__(self, (x,y,r), start_theta=0, end_theta=0):
        self.x = x
        self.y = y
        self.r = r
        self.start_theta=start_theta
        self.end_theta=end_theta
    
class Creep:
    def __init__(self, num=0, HP=10):
        self.num=num
        self.HP = HP
        self.x=0
        self.y=0

    def hurt(HPloss):
        self.HP-=HPloss
        return self.HP>0

class Tower:
    ttypes = ['Normal','Fire','Water','Earth','Wind']
                    
    def __init__ (self, ttype = 0, trange = 10, damage = 5):
        self.x = 0
        self.y = 0
        self.type = ttype
        self.delay = 2
        self.firetime = time.time() + self.delay
        if ttype == 0:
            self.range = 15
            self.damage = 10
        elif ttype == 1:
            self.range = 20
            self.damage = 8
        elif ttype == 2:
            self.range = 10
            self.damage = 10
        elif ttype == 3:
            self.range = 15
            self.damage = 10
        elif ttype == 4:
            self.range = 10
            self.damage = 30
        self.target = None #but don't shoot at it til it's in range!
        

    def __str__(self):
        print 'This', ttypes[self.ttype], 'tower is located at (', self.x, ',', self.y, ').'

    def update(self):
        if self.target == None:
            self.target = selecttarget()
        #is the current creep dead?
        if self.target.HP <= 0:
        #if so, find a new creep
            self.selecttarget()
        self.aim()
        if time.time() - self.firetime > self.delay:
            self.shoot


    def shoot(self):
        self.target.hurt(self.damage)
        self.firetime = time.time()


    def selecttarget(self):
        #Choose the first living creep in range
        for creep in board.creeps:
            if creep.HP > 0:
                if math.sqrt( (creep.x - self.x)**2 + (creep.y - self.y)**2) <= self.range:
                    theta = atan2(self.target.x - self.x, self.target.y - self.y)
                    if -math.pi/2 < theta < math.pi/2:
                        self.target = creep
                        return
        self.target = None
        return None         #We need to deal with the end of the game.


    def aim(self):
        theta = atan2(self.target.x - self.x, self.target.y - self.y)
        SERVO_PLACEMENT = 135-(theta * 110) / 255.0;
        board.towerlist.append(SERVO_PLACEMENT, self.address)
        #convert to duty cycle and write it to the list for the pic
        pass

##    class Fire(Tower):
##        def __init__(self):
##            self.range =
##            self.damage =
##
##    class Water(Tower):
##        def __init__(self):
##            self.range =
##            self.damage =
##
##    class Wind(Tower):
##        def __init__(self):
##            self.range =
##            self.damage =
##
##    class Earth(Tower):
##        def __init__(self):
##            self.range =
##            self.damage =
    
    
towergui=TowerGUI()        
        
